﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Lab1;

namespace box_test
{
    class XWindow : System.Windows.Forms.Form
    {

        private  const string cubeTexture = @"d:\x\Metal7.bmp";
        private  const string coneTexture = @"d:\x\Concgray.bmp";
        private  const string cylinderTexture = @"d:\x\Oldwood.bmp";
        private  const string bezierTexture = @"d:\x\Travertn.bmp";

        private Device device = null;
        private System.ComponentModel.Container components = null;

        private Vector3 center, boxMin, boxMax;
        private float radius;

        public XWindow()
        {
            InitializeComponents();
        }

        private void InitializeComponents()
        {
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);

            this.ClientSize = new System.Drawing.Size(500, 500);

        }

        public void InitializeDevice()
        {
            PresentParameters parameters = new PresentParameters();
            parameters.Windowed = true;
            parameters.SwapEffect = SwapEffect.Discard;
            parameters.AutoDepthStencilFormat = DepthFormat.D16;
            parameters.EnableAutoDepthStencil = true;
            device = new Device(0, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, parameters);
            device.RenderState.CullMode = Cull.None;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }

            base.Dispose(disposing);
        }
        float t = 0;
        public void InitializeCamera() // To jest nowe w tej wersji. Tworzymy kamerę.
        {
            float aspect_ratio = this.Width / (float)this.Height;
            float q = 2.0f + (float)Math.Sin(4 * t); //q - pomocnicza zmienna sterująca ruchem kamery
            device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, aspect_ratio, 1f, radius * 12);  // macierz projekcji
            device.Transform.View = Matrix.LookAtLH(
                center - (new Vector3(3 * radius * (float)Math.Cos(t), 3 * radius * (float)Math.Sin(t), (float)3 * q * radius * (float)Math.Sin(t))),
                center,
                new Vector3(0, 1, 0)); // macierz widoku

            device.Transform.World = Matrix.Identity;

            device.Lights[0].Type = LightType.Directional;  // tworzymy dwa światła kierunkowe
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Direction = new Vector3(1, 1, 1);
            device.Lights[0].Enabled = true;
            device.Lights[1].Type = LightType.Directional;
            device.Lights[1].Diffuse = Color.White;
            device.Lights[1].Direction = new Vector3(-1, -1, -1);
            device.Lights[1].Enabled = true;
            t += 0.02f;

        }
        protected override void OnPaint(PaintEventArgs e)
        {
            InitializeCamera();
            Material material = new Material();

            material.Diffuse = Color.White;
            material.Specular = Color.LightGray;
            material.SpecularSharpness = 15.0F;

            device.Material = material;
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, System.Drawing.Color.DarkSlateBlue, 1.0f, 0);
            device.BeginScene();

            device.VertexFormat = CustomVertex.PositionNormalTextured.Format;

            foreach (Face f in faces)
            {
                device.DrawUserPrimitives(PrimitiveType.TriangleList, f.numberOfTriangles, f.getVertices());
            }

            //device.SetStreamSource(0, vBuffer, 0);
            //device.DrawPrimitives(PrimitiveType.TriangleFan, 0, 2);
            //device.DrawPrimitives(PrimitiveType.TriangleFan, 4, 2);
            //device.DrawPrimitives(PrimitiveType.TriangleList, 8, 8);
            device.EndScene();
            device.Present();
            this.Invalidate();
        }

        public Vector3 ComputeNormal(Vector3 a, Vector3 b)
        {
            Vector3 n = new Vector3(a.Y * b.Z - a.Z * b.Y, a.Z * b.X - a.X * b.Z, a.X * b.Y - a.Y * b.X);
            return n;
        }
        public Vector3 ComputeNormal(Vector3 v1, Vector3 v2, Vector3 v3)
        {
            Vector3 a = v2 - v1;
            Vector3 b = v3 - v1;
            Vector3 n = Vector3.Cross(a, b);
            n.Normalize();
            return n;
           // return ComputeNormal(a, b);
        }

        private List<Face> faces = new List<Face>();
        public void CreateBox(float a, float b, float c, bool isTextured)
        {
            if (isTextured)
            {
                Texture texture = TextureLoader.FromFile(device, cubeTexture);
                device.SetTexture(0, texture);
            }

            Face baseFace = new Face();
            baseFace.AddVertex(new Vector3(-a / 2, -b / 2, -c / 2), new Vector3(), 0.5F, 0);
            baseFace.AddVertex(new Vector3(a / 2, -b / 2, -c / 2), new Vector3(), 0.5F, 0.5F);
            baseFace.AddVertex(new Vector3(a / 2, b / 2, -c / 2), new Vector3(), 0, 0.5F);

            baseFace.AddVertex(new Vector3(a / 2, b / 2, -c / 2), new Vector3(), 0, 0.5F);
            baseFace.AddVertex(new Vector3(-a / 2, b / 2, -c / 2), new Vector3(), 0, 0);
            baseFace.AddVertex(new Vector3(-a / 2, -b / 2, -c / 2), new Vector3(), 0.5F, 0);

            Face face1 = baseFace.TranslateX(a/2);
            face1 = face1.TranslateZ(c / 2);
            face1 = face1.RotateY(270);
            face1 = face1.TranslateZ(-c / 2);
            face1 = face1.TranslateX(-a / 2);
            Face face2 = baseFace.TranslateX(-a / 2);
            face2 = face2.TranslateZ(c / 2);
            face2 = face2.RotateY(90);
            face2 = face2.TranslateZ(-c / 2);
            face2 = face2.TranslateX(a / 2);
            Face face3 = baseFace.TranslateY(b / 2);
            face3 = face3.TranslateZ(c / 2);
            face3 = face3.RotateX(90);
            face3 = face3.TranslateZ(-c / 2);
            face3 = face3.TranslateY(-b / 2);
            Face face4 = baseFace.TranslateY(-b / 2);
            face4 = face4.TranslateZ(c / 2);
            face4 = face4.RotateX(270);
            face4 = face4.TranslateZ(-c / 2);
            face4 = face4.TranslateY(b / 2);
            face1.adjustHeight(c);
            face2.adjustHeight(c);
            face3.adjustHeight(c);
            face4.adjustHeight(c);


            Face topFace = baseFace.TranslateZ(c);

            faces.Add(baseFace);
            faces.Add(face1);
            faces.Add(face2);
            faces.Add(face3);
            faces.Add(face4);
            faces.Add(topFace);

            List<CustomVertex.PositionNormalTextured> total = new List<CustomVertex.PositionNormalTextured>();
            foreach(Face f in faces){
                CustomVertex.PositionNormalTextured[] faceArray = f.getVertices();
                for (int n = 1; n < faceArray.Length-1; n++)
                {
                    Vector3 normal = ComputeNormal(faceArray[n - 1].Position, faceArray[n].Position, faceArray[n + 1].Position);
                    f.UpdateNormal(normal);
                }
                total.AddRange(faceArray);
            }

            Geometry.ComputeBoundingBox(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out boxMin, out boxMax);
            radius = Geometry.ComputeBoundingSphere(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out center);
        }

        public void CreateCone(int basePrecision, float height, float width, bool isTextured)
        {
            if (isTextured)
            {
                Texture texture = TextureLoader.FromFile(device, coneTexture);
                device.SetTexture(0, texture);
            }

            Face baseFace = new Face();

            float baseSpot = height/2.0f;
            float r = width/2.0f;
            float phi = (float) (2 * Math.PI / basePrecision);
            float angle = 0;
            for (int i = 0; i < basePrecision + 2; i++)
            {
                Vector3 baseVertex = new Vector3(0, -baseSpot, 0);
                Vector3 v1 = new Vector3();
                v1.X = (float)(r * Math.Cos(angle));
                v1.Y = -baseSpot;
                v1.Z = -(float)(r * Math.Sin(angle));
                angle += phi;
                Vector3 v2 = new Vector3();
                v2.X = (float)(r * Math.Cos(angle));
                v2.Y = -baseSpot;
                v2.Z = -(float)(r * Math.Sin(angle));
                baseFace.AddVertex(baseVertex, new Vector3(), 0.5F, 0.5F);
                baseFace.AddVertex(v1, new Vector3(), (float)Math.Cos(angle - phi), (float)Math.Sin(angle - phi));
                baseFace.AddVertex(v2, new Vector3(), (float)Math.Cos(angle), (float)Math.Sin(angle));
            }
            faces.Add(baseFace);
            Face sideFace = new Face();
            Vector3 top = new Vector3(0, baseSpot, 0);
            Vector3 rimV1 = baseFace.getVertices()[1].Position;
            Vector3 rimV2 = baseFace.getVertices()[2].Position;
            sideFace.AddVertex(top, new Vector3(), 0.5F, 0.5F);
            sideFace.AddVertex(rimV1, new Vector3(), baseFace.getVertices()[1].Tu, baseFace.getVertices()[1].Tv);
            sideFace.AddVertex(rimV2, new Vector3(), baseFace.getVertices()[2].Tu, baseFace.getVertices()[2].Tv);
            faces.Add(sideFace);
            float degreesPhi = 360 / basePrecision;
            float degreesAngle = 0;
            for (int i = 0; i < basePrecision+2; i++)
            {
                Face rotatedSideFace = sideFace.RotateY(degreesAngle);
                Face texturedFace = new Face();
                CustomVertex.PositionNormalTextured[] verts = rotatedSideFace.getVertices();
                texturedFace.AddVertex(verts[0].Position, verts[0].Normal, 0.5F, 0.5F);
                texturedFace.AddVertex(verts[1].Position, verts[1].Normal, (float)Math.Cos(angle), (float)Math.Sin(angle));
                texturedFace.AddVertex(verts[2].Position, verts[2].Normal, (float)Math.Cos(angle + phi), (float)Math.Sin(angle + phi));
                degreesAngle += degreesPhi;
                angle += phi;

                faces.Add(texturedFace);
            }

            List<CustomVertex.PositionNormalTextured> total = new List<CustomVertex.PositionNormalTextured>();
            foreach (Face f in faces)
            {
                CustomVertex.PositionNormalTextured[] faceArray = f.getVertices();
                for (int n = 1; n < faceArray.Length - 1; n++)
                {
                    Vector3 normal = ComputeNormal(faceArray[n - 1].Position, faceArray[n].Position, faceArray[n + 1].Position);
                    f.UpdateNormal(normal);
                }
                total.AddRange(faceArray);
            }
            Geometry.ComputeBoundingBox(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out boxMin, out boxMax);
            radius = Geometry.ComputeBoundingSphere(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out center);
        }

        public void CreateCylinder(int basePrecision, float height, float width, bool isTextured)
        {
            if (isTextured)
            {
                Texture texture = TextureLoader.FromFile(device, cylinderTexture);
                device.SetTexture(0, texture);
            }

            Face baseFace = new Face();

            float baseSpot = height / 2.0f;
            float r = width / 2.0f;
            float phi = (float)(2 * Math.PI / basePrecision);
            float angle = 0;
            for (int i = 0; i < basePrecision + 2; i++)
            {
                Vector3 baseVertex = new Vector3(0, -baseSpot, 0);
                Vector3 v1 = new Vector3();
                v1.X = (float)(r * Math.Cos(angle));
                v1.Y = -baseSpot;
                v1.Z = -(float)(r * Math.Sin(angle));
                angle += phi;
                Vector3 v2 = new Vector3();
                v2.X = (float)(r * Math.Cos(angle));
                v2.Y = -baseSpot;
                v2.Z = -(float)(r * Math.Sin(angle));
                baseFace.AddVertex(baseVertex, new Vector3(), 0.5F, 0.5F);
                baseFace.AddVertex(v1, new Vector3(), (float)Math.Cos(angle-phi), (float)Math.Sin(angle-phi));
                baseFace.AddVertex(v2, new Vector3(), (float)Math.Cos(angle), (float)Math.Sin(angle));
            }
            faces.Add(baseFace);
            Face topFace = baseFace.TranslateY(height);
            faces.Add(topFace);

            int baseVertexPointer = 1;
            int topVertexPointer = 1;
            CustomVertex.PositionNormalTextured[] baseVertices = baseFace.getVertices();
            CustomVertex.PositionNormalTextured[] topVertices = topFace.getVertices();
            float textureStep = 1 / (float)basePrecision;
            float textureCoord = 0;
            for (int i = 0; i < basePrecision + 2; i++)
            {
                Face sideFace = new Face();
                sideFace.AddVertex(baseVertices[baseVertexPointer].Position, new Vector3(), 0, textureCoord);
                sideFace.AddVertex(baseVertices[baseVertexPointer + 1].Position, new Vector3(), 0, textureCoord + textureStep);
                sideFace.AddVertex(topVertices[topVertexPointer].Position, new Vector3(), 1, textureCoord);

                sideFace.AddVertex(topVertices[topVertexPointer].Position, new Vector3(), 1, textureCoord);
                sideFace.AddVertex(topVertices[topVertexPointer + 1].Position, new Vector3(), 1, textureCoord + textureStep);
                sideFace.AddVertex(baseVertices[baseVertexPointer + 1].Position, new Vector3(), 0, textureCoord + textureStep);
                faces.Add(sideFace);
                baseVertexPointer += 3;
                topVertexPointer += 3;
                textureCoord += textureStep;
            }

            List<CustomVertex.PositionNormalTextured> total = new List<CustomVertex.PositionNormalTextured>();
            foreach (Face f in faces)
            {
                CustomVertex.PositionNormalTextured[] faceArray = f.getVertices();
                for (int n = 1; n < faceArray.Length - 1; n++)
                {
                    Vector3 normal = ComputeNormal(faceArray[n - 1].Position, faceArray[n].Position, faceArray[n + 1].Position);
                    f.UpdateNormal(normal);
                }
                total.AddRange(faceArray);
            }
            Geometry.ComputeBoundingBox(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out boxMin, out boxMax);
            radius = Geometry.ComputeBoundingSphere(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out center);
        }

        public void CreateBezierSurface(int basePrecision, Vector3[] mnPoints, int row, bool isTextured)
        {
            if (isTextured)
            {
                Texture texture = TextureLoader.FromFile(device, bezierTexture);
                device.SetTexture(0, texture);
            }

            int M = row;
            int N = mnPoints.Length/row;
            
            float unit = 1/(float)basePrecision;

            List<Vector3> vertices = new List<Vector3>();

            for (float u = 0; u < 1; u+=unit)
            {
                for (float v = 0; v < 1; v+=unit)
                {
                    Vector3 vertex = new Vector3(0,0,0);
                    for (int m = 0; m < M; m++)
                    {
                        for (int n = 0; n < N; n++)
                        {
                            Vector3 K = mnPoints[m * row + n];
                            float b1 = BernsteinPoly(u, N, n);
                            float b2 = BernsteinPoly(v, M, m);

                            K.Multiply(b1 * b2);
                            vertex += K;
                        }
                    }
                    vertex.X = u;
                    vertex.Z = v;
                    vertices.Add(vertex);
                }
            }

            Vector3[] vertArray = vertices.ToArray();
            for (int upRow = 0, downRow = 1; downRow < basePrecision;  upRow++, downRow++)
            {
                for (int i = 0, j = 0; j < basePrecision-1; i++, j++)
                {
                    Face face = new Face();
                    face.AddVertex(vertArray[upRow * basePrecision + i], new Vector3(), upRow / (float)basePrecision, i / (float)basePrecision);
                    face.AddVertex(vertArray[upRow * basePrecision + i + 1], new Vector3(), upRow / (float)basePrecision, (i+1) / (float)basePrecision);
                    face.AddVertex(vertArray[downRow * basePrecision + j], new Vector3(), downRow / (float)basePrecision, (j) / (float)basePrecision);
                    faces.Add(face);

                    face = new Face();
                    face.AddVertex(vertArray[upRow * basePrecision + i + 1], new Vector3(), upRow/(float)basePrecision,(i+1)/(float)basePrecision);
                    face.AddVertex(vertArray[downRow * basePrecision + j + 1], new Vector3(), downRow / (float)basePrecision, (j + 1) / (float)basePrecision);
                    face.AddVertex(vertArray[downRow * basePrecision + j], new Vector3(), downRow / (float)basePrecision, (j) / (float)basePrecision);
                    faces.Add(face);
                }
            }

            List<CustomVertex.PositionNormalTextured> total = new List<CustomVertex.PositionNormalTextured>();
            foreach (Face f in faces)
            {
                CustomVertex.PositionNormalTextured[] faceArray = f.getVertices();
                for (int n = 1; n < faceArray.Length - 1; n++)
                {
                    Vector3 normal = ComputeNormal(faceArray[n - 1].Position, faceArray[n].Position, faceArray[n + 1].Position);
                    f.UpdateNormal(normal);
                }
                total.AddRange(faceArray);
            }
            Geometry.ComputeBoundingBox(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out boxMin, out boxMax);
            radius = Geometry.ComputeBoundingSphere(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out center);

        }

        private float BernsteinPoly(float u, int n, int i)
        {
            float facN = Factorial(n);
            float facI = Factorial(i);
            float facNI = Factorial(n-i);

            float poly = (float)((facN / (facI * facNI)) * Math.Pow(u, i) * Math.Pow((1 - u), (n - i)));
            return poly;
        }

        private float Factorial(int value)
        {
            if (value == 0)
            {
                return 1;
            }
            int fac = 1;
            for (int i = 1; i <= value; i++)
            {
                fac *= i;
            }
            return fac;
        }

        public void CreateTorus(int basePrecision, float R, float r, bool isTextured)
        {
            if (isTextured)
            {
                Texture texture = TextureLoader.FromFile(device, bezierTexture);
                device.SetTexture(0, texture);
            }

            float unit = (float)(1 / (float)basePrecision);

            List<Vector3> vertices = new List<Vector3>();

            //for (float u = 0; u < 1 + unit; u += unit)
            int iu = 0;
            for (float u = 0; iu < basePrecision; iu++, u += unit)
            {
                //for (float v = 0; v < 1 + unit; v += unit)
                int iv = 0;
                for (float v = 0; iv < basePrecision; iv++, v += unit)
                {
                    Vector3 p = new Vector3();
                    p.X = (float)((R + r * Math.Cos(2 * Math.PI * v)) * Math.Cos(2 * Math.PI * u));
                    p.Y = (float)((R + r * Math.Cos(2 * Math.PI * v)) * Math.Sin(2 * Math.PI * u));
                    p.Z = (float)(r * Math.Sin(2 * Math.PI * v));
                    vertices.Add(p);
                }
            }
            Vector3[] vertArray = vertices.ToArray();
            int vertArrSize = vertArray.Length;
            for (int upRow = 0, downRow = 1; downRow < basePrecision+1; upRow++, downRow++)
            {
                for (int i = 0, j = 0; j < basePrecision; i++, j++)
                {
                    Face face = new Face();
                    face.AddVertex(vertArray[(upRow * basePrecision + i)%vertArrSize], new Vector3(), upRow / (float)basePrecision, i / (float)basePrecision);
                    face.AddVertex(vertArray[(upRow * basePrecision + i + 1) % vertArrSize], new Vector3(), upRow / (float)basePrecision, (i + 1) / (float)basePrecision);
                    face.AddVertex(vertArray[(downRow * basePrecision + j)%vertArrSize], new Vector3(), downRow / (float)basePrecision, (j) / (float)basePrecision);
                    faces.Add(face);

                    face = new Face();
                    face.AddVertex(vertArray[(upRow * basePrecision + i + 1) % vertArrSize], new Vector3(), upRow / (float)basePrecision, (i + 1) / (float)basePrecision);
                    face.AddVertex(vertArray[(downRow * basePrecision + j + 1)%vertArrSize], new Vector3(), downRow / (float)basePrecision, (j + 1) / (float)basePrecision);
                    face.AddVertex(vertArray[(downRow * basePrecision + j) % vertArrSize], new Vector3(), downRow / (float)basePrecision, (j) / (float)basePrecision);
                    faces.Add(face);
                }
            }
            List<CustomVertex.PositionNormalTextured> total = new List<CustomVertex.PositionNormalTextured>();
            int faceOrdinal = 0;
            foreach (Face f in faces)
            {
                CustomVertex.PositionNormalTextured[] faceArray = f.getVertices();
                for (int n = 1; n < faceArray.Length - 1; n++)
                {
                    Vector3 normal = new Vector3();
                    normal = ComputeNormal(faceArray[n - 1].Position, faceArray[n].Position, faceArray[n + 1].Position);
                    f.UpdateNormal(normal);
                }
                faceOrdinal++;
                total.AddRange(faceArray);
            }
            Geometry.ComputeBoundingBox(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out boxMin, out boxMax);
            radius = Geometry.ComputeBoundingSphere(total.ToArray(), CustomVertex.PositionNormalTextured.Format, out center);

        }


        //public static void Main()
        //{
        //    using (XWindow xwin = new XWindow())
        //    {
        //        xwin.InitializeDevice();
        //        xwin.CreateBox(1, 1, 1);
        //        //xwin.CreateCone(50, 15, 10);
        //        //xwin.CreateCylinder(50, 15, 10);
        //        //xwin.CreateBezierSurface(20, new Vector3[]{
        //        //    new Vector3(1,1,1),
        //        //    new Vector3(1,1,1),
        //        //    new Vector3(1,1,1),

        //        //    new Vector3(1,1,1),
        //        //    new Vector3(1,1,-1),
        //        //    new Vector3(2,1,-1.5F),

        //        //    new Vector3(0.5F,1,1),
        //        //    new Vector3(1,-2,1),
        //        //    new Vector3(1,1,1)
                
        //        //}, 3);
        //        //xwin.RenderFigure();
        //        //xwin.LoadXFile("D:\\x\\cone.x");
        //        //              xwin.LoadXFile("D:\\SSStudia\\mgr Semestr II\\3d\\x\\vs\\woman.X");
        //        Application.Run(xwin);
        //    }
        //}




    }


}
